#ifndef __COMPILE_RUN_HPP__
#define __COMPILE_RUN_HPP__
// 编译运行整合模块

#include <unistd.h>
#include <jsoncpp/json/json.h>
#include "compiler.hpp"
#include "runner.hpp"
#include "../common/util.hpp"
#include "../common/log.hpp"

namespace ns_compile_and_run
{
    using namespace ns_compiler;
    using namespace ns_runner;
    using namespace ns_log;
    using namespace ns_util;

    class CompileAndRun
    {
    public:
        // 删除临时文件
        static void RemoveFile(const std::string& file_name)
        {
            // 因为临时文件的存在情况存在多种，删除文件采用系统接口unlink，但是需要判断
            std::string src_path = PathUtil::Src(file_name);
            if (FileUtil::IsFileExist(src_path)) unlink(src_path.c_str());
            std::string stdout_path = PathUtil::Stdout(file_name);
            if (FileUtil::IsFileExist(stdout_path)) unlink(stdout_path.c_str());
            std::string stdin_path = PathUtil::Stdin(file_name);
            if (FileUtil::IsFileExist(stdin_path)) unlink(stdin_path.c_str());
            std::string stderr_path = PathUtil::Stderr(file_name);
            if (FileUtil::IsFileExist(stderr_path)) unlink(stderr_path.c_str());
            std::string compilererr_path = PathUtil::CompileError(file_name);
            if (FileUtil::IsFileExist(compilererr_path)) unlink(compilererr_path.c_str());
            std::string exe_path = PathUtil::Exe(file_name);
            if (FileUtil::IsFileExist(exe_path)) unlink(exe_path.c_str());
        }

        // 根据不同的状态码进行解析结果输入信息
        // > 0 运行中断  6-内存超过范围、24-cpu使用时间超时、8-浮点数溢出（除0异常）
        // = 0 运行成功
        // < 0 整个过程，非运行报错（内部错误、编译报错、代码为空）
        static std::string CodeToDesc(int status, const std::string& file_name)
        {
            std::string desc;
            switch(status)
            {
            case 0:
                desc = "运行成功！";
                break;
            case -1:
                desc = "代码为空";
                break;
            case -2:
                desc = "未知错误";
                break;
            case -3:
                desc = "编译报错\n";
                desc += FileUtil::ReadFile(PathUtil::CompileError(file_name), true);
                break;
            case 6:
                desc = "内存超过范围";
                break;
            case 24:
                desc = "时间超时";
                break;
            case 8:
                desc = "浮点数溢出";
                break;
            case 11:
                desc = "野指针错误";
                break;
            default:
                desc = "未处理的报错-status为:" + std::to_string(status);
                break;
            }
            return desc;
        }

        /***********************
         * 编译运行模块
         * 输入：in_json输入序列化串  *out_json 输出序列化串(输出参数)
         * 输出
         * in_json  {"code":..., "input":..., "cpu_limit":..., "mem_limit":...}
         * out_json {"status":..., "reason":...[, "stdout":..., "stderr":...]}
         ***********************/
        static void Start(const std::string& in_json, std::string* out_json)
        {
            std::string code, input, file_name;
            int cpu_limit, mem_limit;  // s, kb
            int status, run_reason;  // 状态码, 运行返回码

            // 首先反序列化in_json,提取信息
            Json::Value in_value;
            Json::Reader read;
            if (!read.parse(in_json, in_value))  // 将输入序列化串反序列化
            {
                // 反序列化失败 报错-内部报错
                status = -2;
                goto END;
            }
            code = in_value["code"].asString();
            input = in_value["input"].asString();
            cpu_limit = in_value["cpu_limit"].asInt();
            mem_limit = in_value["mem_limit"].asInt();
            
            // 首先检测code是否存在数据
            if(code.empty())
            {
                status = -1;  // 用户错误
                goto END;
            }
            // 首先生成唯一的临时文件名
            file_name = FileUtil::UniqueFileName();
            // code写入文件
            if(!FileUtil::WriteFile(PathUtil::Src(file_name), code))
            {
                // 如果写入失败，内部报错
                status = -2;
                goto END;
            }

            // 写入文件成功的话，我们进行编译步骤
            if (!Compiler::compile(file_name))
            {
                status = -3;  // 编译失败，导入结果compile_error
                goto END;
            }

            // 将input标准输入写入文件
            if(!FileUtil::WriteFile(PathUtil::Stdin(file_name), input))
            {
                // 如果写入失败，内部报错
                status = -2;
                goto END;
            }

            // 编译步骤成功，我们进行运行步骤
            run_reason = Runner::Run(file_name, cpu_limit, mem_limit);
            if (run_reason < 0)
            {
                // 内部错误
                status = -2;
            }
            else
            {
                // 运行成功或者中断
                status = run_reason;
            }

            END:
            // 构建返回序列化串
            Json::Value out_value;
            out_value["status"] = status;
            out_value["reason"] = CodeToDesc(status, file_name);  // 需要接口
            // 如果运行成功，那么构建可选字段
            if (status == 0)
            {
                out_value["stdout"] = FileUtil::ReadFile(PathUtil::Stdout(file_name), true);
                out_value["stderr"] = FileUtil::ReadFile(PathUtil::Stderr(file_name), true);
            }
            Json::StyledWriter write;
            *out_json = write.write(out_value);

            RemoveFile(file_name);
        }
    };
}

#endif